<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="Cache-Control" content="no-siteapp">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=1, minimum-scale=1, maximum-scale=1">
<meta name="renderer" content="webkit">
<meta name="google" value="notranslate">
<meta name="robots" content="index,follow">


<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Akkuman">
<meta name="twitter:description" content="Akkuman的技术博客">
<meta name="twitter:image:src" content="http://127.0.0.1:8000/images/avatar.png">

<meta property="og:url" content="http://127.0.0.1:8000">
<meta property="og:title" content="Akkuman">
<meta property="og:description" content="Akkuman的技术博客">
<meta property="og:site_name" content="Akkuman">
<meta property="og:image" content="http://127.0.0.1:8000/images/avatar.png">
<meta property="og:type" content="website">
<meta name="robots" content="noodp">

<meta itemprop="name" content="Akkuman">
<meta itemprop="description" content="Akkuman的技术博客">
<meta itemprop="image" content="http://127.0.0.1:8000/images/avatar.png">

<link rel="canonical" href="http://127.0.0.1:8000">

<link rel="shortcut icon" href="/favicon.png">
<link rel="apple-itouch-icon" href="/favicon.png">

<link type="text/css" rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.min.css">
<link type="text/css" rel="stylesheet" href="/bundle/css/prism.css">
<link type="text/css" rel="stylesheet" href="/bundle/css/zoom.css">
<link type="text/css" rel="stylesheet" href="/bundle/css/main.css">
<script src="https://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script>



<script>var cPlayers = [];var cPlayerOptions = [];</script>


<script type="text/javascript">
    var timeSinceLang = {
        year: '年前',
        month: '个月前',
        day: '天前',
        hour: '小时前',
        minute: '分钟前',
        second: '秒前'
    };
    var root = '';
</script>


        <meta name="keywords" content="Python,">
        <meta name="description" content="Python之Requests的高级用法">
        <meta name="author" content="Akkuman">
        <title>Python之Requests的高级用法</title>
    </head>
    <body>
        
        <header id="header" class="clearfix">
  <div class="container-fluid">
      <div class="row">
          <div class="logo">
              <div class="header-logo">
                <script>
                  var getwbclass = function() {
                    var wbclass = ['b', 'w'];
                    return wbclass[Math.floor(Math.random()*wbclass.length)];
                  }
                  var sitetitle = "Akkuman";
                  for (i in sitetitle) {
                    document.write('<a href="/"><span class="' + getwbclass() + ' titlechar">' + sitetitle.charAt(i) + '</span></a>');
                  }          
                  
                </script>
                
                <a id="btn-menu" href="javascript:isMenu();">
                    <span class="b">·</span>
                </a>
                <a href="javascript:isMenu1();">
                    <span id="menu-1" class="bf">1</span>
                </a>
                <a href="javascript:isMenu2();">
                    <span id="menu-2" class="bf">2</span>
                </a>
                <a href="javascript:isMenu3();">
                    <span id="menu-3" class="bf">3</span>
                </a>
              </div>
              <div id="menu-page">
                <a href="/archive.html"><li>归档</li></a>
                <a href="/tag.html"><li>标签</li></a>
                
                <a href="/atom.xml"><li>订阅</li></a>
                
                <a href="about.html"><li>关于</li></a>
              </div>
              <div id="search-box">
                  <div id="search">
                      <input autocomplete="off" type="text" name="s" id="menu-search" placeholder="搜索..." data-root="" />
                  </div>
              </div>
          </div>
      </div>
  </div>
  </header>
        <div id="body" class="clearfix">
            <div class="container-fluid">
                <div class="row">
                    <div id="main" class="col-12 clearfix" role="main">
                        <article class="posti" itemscope itemtype="http://schema.org/BlogPosting">
                            <h1 class="post-title" itemprop="name headline">Python之Requests的高级用法</h1>
                            <div class="post-meta">
                                <p>
                                    Written by <a itemprop="name" href="/about.me.html" rel="author">Akkuman</a> with ♥ on <time datetime="1465562284" itemprop="datePublished"></time> in <a href="/tag/Python/index.html">Python </a>
                                </p>
                            </div>
                            <div class="post-content" itemprop="articleBody">
                                <hr />

<h1>高级用法</h1>

<hr />

<p>本篇文档涵盖了Requests的一些更加高级的特性。</p>

<hr />

<h2>会话对象</h2>

<hr />

<p>会话对象让你能够跨请求保持某些参数。它也会在同一个Session实例发出的所有请求之间保持cookies。</p>

<p>会话对象具有主要的Requests API的所有方法。</p>

<p>我们来跨请求保持一些cookies:</p>

<pre><code class="language-python">s = requests.Session()

s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get(&quot;http://httpbin.org/cookies&quot;)

print(r.text)
# '{&quot;cookies&quot;: {&quot;sessioncookie&quot;: &quot;123456789&quot;}}'
</code></pre>

<p>会话也可用来为请求方法提供缺省数据。这是通过为会话对象的属性提供数据来实现的:</p>

<pre><code class="language-python">s = requests.Session()
s.auth = ('user', 'pass')
s.headers.update({'x-test': 'true'})

# both 'x-test' and 'x-test2' are sent
s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})
</code></pre>

<p>任何你传递给请求方法的字典都会与已设置会话层数据合并。方法层的参数覆盖会话的参数。</p>

<blockquote>
<p><strong>从字典参数中移除一个值</strong>
有时你会想省略字典参数中一些会话层的键。要做到这一点，你只需简单地在方法层参数中将那个键的值设置为 None ，那个键就会被自动省略掉。</p>
</blockquote>

<p>包含在一个会话中的所有数据你都可以直接使用。学习更多细节请阅读 <a href="http://cn.python-requests.org/zh_CN/latest/api.html#sessionapi">会话API文档</a> 。</p>

<hr />

<h2>请求与响应对象</h2>

<hr />

<p>任何时候调用requests.*()你都在做两件主要的事情。其一，你在构建一个 Request 对象， 该对象将被发送到某个服务器请求或查询一些资源。其二，一旦 requests 得到一个从 服务器返回的响应就会产生一个 Response 对象。该响应对象包含服务器返回的所有信息， 也包含你原来创建的 Request 对象。如下是一个简单的请求，从Wikipedia的服务器得到 一些非常重要的信息:</p>

<pre><code class="language-python">&gt;&gt;&gt; r = requests.get('http://en.wikipedia.org/wiki/Monty_Python')
</code></pre>

<p>如果想访问服务器返回给我们的响应头部信息，可以这样做:</p>

<pre><code class="language-python">&gt;&gt;&gt; r.headers
{'content-length': '56170', 'x-content-type-options': 'nosniff', 'x-cache':
'HIT from cp1006.eqiad.wmnet, MISS from cp1010.eqiad.wmnet', 'content-encoding':
'gzip', 'age': '3080', 'content-language': 'en', 'vary': 'Accept-Encoding,Cookie',
'server': 'Apache', 'last-modified': 'Wed, 13 Jun 2012 01:33:50 GMT',
'connection': 'close', 'cache-control': 'private, s-maxage=0, max-age=0,
must-revalidate', 'date': 'Thu, 14 Jun 2012 12:59:39 GMT', 'content-type':
'text/html; charset=UTF-8', 'x-cache-lookup': 'HIT from cp1006.eqiad.wmnet:3128,
MISS from cp1010.eqiad.wmnet:80'}
</code></pre>

<p>然而，如果想得到发送到服务器的请求的头部，我们可以简单地访问该请求，然后是该请求的头部:</p>

<pre><code class="language-python">&gt;&gt;&gt; r.request.headers
{'Accept-Encoding': 'identity, deflate, compress, gzip',
'Accept': '*/*', 'User-Agent': 'python-requests/0.13.1'}
</code></pre>

<hr />

<h2>Prepared Request</h2>

<hr />

<p>当你从API调用或Session调用得到一个<a href="http://cn.python-requests.org/zh_CN/latest/api.html#requests.Response">Response</a>对象，对于这个的request属性实际上是被使用的PreparedRequest，在某些情况下你可能希望在发送请求之前对body和headers(或其他东西)做些额外的工作，一个简单的例子如下:</p>

<pre><code class="language-python">from requests import Request, Session

s = Session()
req = Request('GET', url,
    data=data,
    headers=header
)
prepped = req.prepare()

# do something with prepped.body
# do something with prepped.headers

resp = s.send(prepped,
    stream=stream,
    verify=verify,
    proxies=proxies,
    cert=cert,
    timeout=timeout
)

print(resp.status_code)
</code></pre>

<p>因为你没有用Request对象做任何特别的事情，你应该立即封装它和修改 PreparedRequest 对象，然后携带着你想要发送到requests.* 或 Session.*的其他参数来发送它</p>

<p>但是，上面的代码会丧失一些Requests Session对象的优势，特别的，Session层的状态比如cookies不会被应用到你的其他请求中，要使它得到应用，你可以用*Session.prepare_request()*来替换 *Request.prepare()*，比如下面的例子:</p>

<pre><code class="language-python">from requests import Request, Session

s = Session()
req = Request('GET',  url,
    data=data
    headers=headers
)

prepped = s.prepare_request(req)

# do something with prepped.body
# do something with prepped.headers

resp = s.send(prepped,
    stream=stream,
    verify=verify,
    proxies=proxies,
    cert=cert,
    timeout=timeout
)

print(resp.status_code)
</code></pre>

<hr />

<h2>SSL证书验证</h2>

<hr />

<p>Requests可以为HTTPS请求验证SSL证书，就像web浏览器一样。要想检查某个主机的SSL证书，你可以使用 verify 参数:</p>

<pre><code class="language-python">&gt;&gt;&gt; requests.get('https://kennethreitz.com', verify=True)
requests.exceptions.SSLError: hostname 'kennethreitz.com' doesn't match either of '*.herokuapp.com', 'herokuapp.com'
</code></pre>

<p>在该域名上我没有设置SSL，所以失败了。但Github设置了SSL:</p>

<pre><code class="language-python">&gt;&gt;&gt; requests.get('https://github.com', verify=True)
&lt;Response [200]&gt;
</code></pre>

<p>对于私有证书，你也可以传递一个*CA_BUNDLE*文件的路径给 <strong>verify</strong> 。你也可以设置 <em>REQUEST_CA_BUNDLE</em> 环境变量。</p>

<p>如果你将<strong>verify</strong>设置为<strong>False</strong>，Requests也能<strong>忽略</strong>对SSL证书的验证。</p>

<pre><code class="language-python">&gt;&gt;&gt; requests.get('https://kennethreitz.com', verify=False)
&lt;Response [200]&gt;
</code></pre>

<p><strong>默认</strong>情况下， <strong>verify</strong> 是设置为<strong>True</strong>的。选项 verify 仅应用于主机证书。</p>

<p>你也可以指定一个本地证书用作客户端证书，可以是单个文件（包含密钥和证书）或一个包含两个文件路径的元组:</p>

<pre><code class="language-python">&gt;&gt;&gt; requests.get('https://kennethreitz.com', cert=('/path/server.crt', '/path/key'))
&lt;Response [200]&gt;
</code></pre>

<p>如果你指定了一个错误路径或一个无效的证书:</p>

<pre><code class="language-python">&gt;&gt;&gt; requests.get('https://kennethreitz.com', cert='/wrong_path/server.pem')
SSLError: [Errno 336265225] _ssl.c:347: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib
</code></pre>

<hr />

<h2>响应体内容工作流</h2>

<hr />

<p>默认情况下，当你进行网络请求后，响应体会<strong>立即</strong>被下载。你可以通过 <strong>stream</strong> 参数覆盖这个行为，<strong>推迟</strong>下载响应体直到访问 <strong>Response.content</strong> 属性:</p>

<pre><code class="language-python">tarball_url = 'https://github.com/kennethreitz/requests/tarball/master'
r = requests.get(tarball_url, stream=True)
</code></pre>

<p>此时仅有响应头被下载下来了，<strong>连接保持打开状态</strong>，因此允许我们根据条件获取内容:</p>

<pre><code class="language-python">if int(r.headers['content-length']) &lt; TOO_LONG:
    content = r.content
    ...
</code></pre>

<p>你可以进一步使用 <a href="http://cn.python-requests.org/zh_CN/latest/api.html#requests.Response.iter_content">Response.iter_content</a> 和 <a href="http://cn.python-requests.org/zh_CN/latest/api.html#requests.Response.iter_lines">Response.iter_lines</a> 方法来控制工作流，或者以 <a href="http://cn.python-requests.org/zh_CN/latest/api.html#requests.Response.raw">Response.raw</a> 从底层urllib3的 urllib3.HTTPResponse &lt;urllib3.response.HTTPResponse 读取。</p>

<p>如果当你请求时设置<strong>stream</strong>为<strong>True</strong>，Requests将不能释放这个连接为连接池，除非你读取了全部数据或者调用了<strong>Response.close</strong>，这样会使连接变得低效率。如果当你设置 <strong>stream = True</strong> 时你发现你自己部分地读取了响应体数据(或者完全没读取响应体数据)，你应该考虑使用<a href="http://docs.python.org/2/library/contextlib.html#contextlib.closing"><strong><em>contextlib.closing</em></strong></a>,比如下面的例子:</p>

<pre><code class="language-python">from contextlib import closing

with closing(requests.get('http://httpbin.org/get', stream=True)) as r:
    # Do things with the response here.
</code></pre>

<hr />

<h2>保持活动状态（持久连接）</h2>

<hr />

<p>好消息 - 归功于urllib3，<strong>同一会话内的持久连接是完全自动处理的！同一会话内你发出的任何请求都会自动复用恰当的连接！</strong></p>

<blockquote>
<p><strong>注意</strong>：只有所有的响应体数据被读取完毕连接才会被释放为连接池；所以确保将 <strong>stream</strong> 设置为 <strong>False</strong> 或读取 <strong>Response</strong> 对象的 <strong>content</strong> 属性。</p>
</blockquote>

<hr />

<h2>流式上传</h2>

<hr />

<p>Requests支持流式上传，这允许你发送大的数据流或文件而无需先把它们读入内存。要使用流式上传，仅需为你的请求体提供一个类文件对象即可:</p>

<pre><code class="language-python">with open('massive-body') as f:
    requests.post('http://some.url/streamed', data=f)
</code></pre>

<hr />

<h2>块编码请求</h2>

<hr />

<p>对于出去和进来的请求，Requests也支持分块传输编码。要发送一个块编码的请求，仅需为你的请求体提供一个生成器（或任意没有具体长度(without a length)的迭代器）:</p>

<pre><code class="language-python">def gen():
    yield 'hi'
    yield 'there'

requests.post('http://some.url/chunked', data=gen())
</code></pre>

<hr />

<h2>POST 多个编码(Multipart-Encoded)文件</h2>

<hr />

<p>你可以在一个请求中发送多个文件，例如，假设你希望上传图像文件到一个包含多个文件字段‘images’的HTML表单</p>

<pre><code class="language-html">&lt;input type=”file” name=”images” multiple=”true” required=”true”/&gt;
</code></pre>

<p>达到这个目的，仅仅只需要设置文件到一个包含(<em>form_field_name</em>, <em>file_info</em>)的元组的<strong>列表</strong>：</p>

<pre><code class="language-python">&gt;&gt;&gt; url = 'http://httpbin.org/post'
&gt;&gt;&gt; multiple_files = [('images', ('foo.png', open('foo.png', 'rb'), 'image/png')),
                      ('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))]
&gt;&gt;&gt; r = requests.post(url, files=multiple_files)
&gt;&gt;&gt; r.text
{
  ...
  'files': {'images': ' ....'}
  'Content-Type': 'multipart/form-data; boundary=3131623adb2043caaeb5538cc7aa0b3a',
  ...
}
</code></pre>

<hr />

<h2>事件挂钩</h2>

<hr />

<p>Requests有一个钩子系统，你可以用来操控部分请求过程，或信号事件处理。</p>

<p>可用的钩子:</p>

<p><em>response:</em></p>

<p>从一个请求产生的响应</p>

<p>你可以通过传递一个 <em>{hook_name: callback_function}</em> 字典给 hooks 请求参数 为每个请求分配一个钩子函数:</p>

<pre><code class="language-python">hooks=dict(response=print_url)
</code></pre>

<p><em>callback_function</em> 会接受一个数据块作为它的第一个参数。</p>

<pre><code class="language-python">def print_url(r):
    print(r.url)
</code></pre>

<p>若执行你的回调函数期间发生错误，系统会给出一个警告。</p>

<p>若回调函数返回一个值，默认以该值替换传进来的数据。若函数未返回任何东西， 也没有什么其他的影响。</p>

<p>我们来在运行期间打印一些请求方法的参数:</p>

<pre><code class="language-python">&gt;&gt;&gt; requests.get('http://httpbin.org', hooks=dict(response=print_url))
http://httpbin.org
&lt;Response [200]&gt;
</code></pre>

<hr />

<h2>自定义身份验证</h2>

<hr />

<p>Requests允许你使用自己指定的身份验证机制。</p>

<p>任何传递给请求方法的 <strong><em>auth</em></strong> 参数的可调用对象，在请求发出之前都有机会修改请求。</p>

<p>自定义的身份验证机制是作为 <em>requests.auth.AuthBase</em> 的子类来实现的，也非常容易定义。</p>

<p>Requests在 <em>requests.auth</em> 中提供了两种常见的的身份验证方案： <em>HTTPBasicAuth</em> 和 <em>HTTPDigestAuth</em> 。</p>

<p>假设我们有一个web服务，仅在 X-Pizza 头被设置为一个密码值的情况下才会有响应。虽然这不太可能， 但就以它为例好了</p>

<pre><code class="language-python">from requests.auth import AuthBase

class PizzaAuth(AuthBase):
    &quot;&quot;&quot;Attaches HTTP Pizza Authentication to the given Request object.&quot;&quot;&quot;
    def __init__(self, username):
        # setup any auth-related data here
        self.username = username

    def __call__(self, r):
        # modify and return the request
        r.headers['X-Pizza'] = self.username
        return r
</code></pre>

<p>然后就可以使用我们的<strong><em>PizzaAuth</em></strong>来进行网络请求:</p>

<pre><code class="language-python">&gt;&gt;&gt; requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth'))
&lt;Response [200]&gt;
</code></pre>

<hr />

<h2>流式请求</h2>

<hr />

<p>使用 <a href="http://cn.python-requests.org/zh_CN/latest/api.html#requests.Response.iter_lines">requests.Response.iter_lines()</a> 你可以很方便地对流式API（例如 <a href="https://dev.twittercom/docs/streaming-api">Twitter的流式API</a> ）进行迭代。简单地设置 stream 为 True 便可以使用 <a href="http://cn.python-requests.org/zh_CN/latest/api.html#requests.Response.iter_lines">iter_lines()</a> 对相应进行迭代:</p>

<pre><code class="language-python">import json
import requests

r = requests.get('http://httpbin.org/stream/20', stream=True)

for line in r.iter_lines():

    # filter out keep-alive new lines
    if line:
        print(json.loads(line))
</code></pre>

<hr />

<h2>代理</h2>

<hr />

<p>如果需要使用代理，你可以通过为任意请求方法提供 <strong><em>proxies</em></strong> 参数来配置单个请求:</p>

<pre><code class="language-python">import requests

proxies = {
  &quot;http&quot;: &quot;http://10.10.1.10:3128&quot;,
  &quot;https&quot;: &quot;http://10.10.1.10:1080&quot;,
}

requests.get(&quot;http://example.org&quot;, proxies=proxies)
</code></pre>

<p>你也可以通过环境变量 <em>HTTP_PROXY</em> 和 <em>HTTPS_PROXY</em> 来配置代理。</p>

<pre><code class="language-bash">$ export HTTP_PROXY=&quot;http://10.10.1.10:3128&quot;
$ export HTTPS_PROXY=&quot;http://10.10.1.10:1080&quot;
$ python
</code></pre>

<pre><code class="language-python">&gt;&gt;&gt; import requests
&gt;&gt;&gt; requests.get(&quot;http://example.org&quot;)
</code></pre>

<p>若你的代理需要使用<strong>HTTP Basic Auth</strong>，可以使用 <strong><em><a href="http://user:password@host/">http://user:password@host/</a> 语法</em></strong>:</p>

<pre><code class="language-python">proxies = {
    &quot;http&quot;: &quot;http://user:pass@10.10.1.10:3128/&quot;,
}
</code></pre>

<hr />

<h2>合规性</h2>

<hr />

<p>Requests符合所有相关的规范和RFC，这样不会为用户造成不必要的困难。但这种对规范的考虑 导致一些行为对于不熟悉相关规范的人来说看似有点奇怪。</p>

<h3>编码方式</h3>

<p>当你收到一个响应时，Requests会猜测响应的编码方式，用于在你调用 Response.text 方法时 对响应进行解码。Requests首先在HTTP头部检测是否存在指定的编码方式，如果不存在，则会使用 charade 来尝试猜测编码方式。</p>

<p>只有当HTTP头部不存在明确指定的字符集，并且 Content-Type 头部字段包含 text 值之时， Requests才不去猜测编码方式。</p>

<p>在这种情况下， RFC 2616 指定默认字符集 必须是 ISO-8859-1 。Requests遵从这一规范。如果你需要一种不同的编码方式，你可以手动设置 Response.encoding 属性，或使用原始的 Response.content 。<strong>(可结合上一篇安装使用快速上手中的 <em>响应内容</em> 学习)</strong></p>

<hr />

<h2>HTTP请求类型(附加例子)</h2>

<hr />

<p>Requests提供了几乎所有HTTP请求类型的功能：GET，OPTIONS， HEAD，POST，PUT，PATCH和DELETE。 以下内容为使用Requests中的这些请求类型以及Github API提供了<strong>详细示例</strong>。</p>

<p>我将从最常使用的请求类型GET开始。HTTP GET是一个幂等的方法，从给定的URL返回一个资源。因而， 当你试图从一个web位置获取数据之时，你应该使用这个请求类型。一个使用示例是尝试从Github上获取 关于一个特定commit的信息。假设我们想获取Requests的commit a050faf 的信息。我们可以 这样去做:</p>

<pre><code class="language-python">&gt;&gt;&gt; import requests
&gt;&gt;&gt; r = requests.get('https://api.github.com/repos/kennethreitz/requests/git/commits/a050faf084662f3a352dd1a941f2c7c9f886d4ad')
</code></pre>

<p>我们应该确认Github是否正确响应。如果正确响应，我们想弄清响应内容是什么类型的。像这样去做:</p>

<pre><code class="language-python">&gt;&gt;&gt; if (r.status_code == requests.codes.ok):
...     print r.headers['content-type']
...
application/json; charset=utf-8
</code></pre>

<p>可见，GitHub返回了JSON数据，非常好，这样就可以使用 <strong>r.json</strong> 方法把这个返回的数据解析成Python对象。</p>

<pre><code class="language-python">&gt;&gt;&gt; commit_data = r.json()
&gt;&gt;&gt; print commit_data.keys()
[u'committer', u'author', u'url', u'tree', u'sha', u'parents', u'message']
&gt;&gt;&gt; print commit_data[u'committer']
{u'date': u'2012-05-10T11:10:50-07:00', u'email': u'me@kennethreitz.com', u'name': u'Kenneth Reitz'}
&gt;&gt;&gt; print commit_data[u'message']
makin' history
</code></pre>

<p>到目前为止，一切都非常简单。嗯，我们来研究一下GitHub的API。我们可以去看看文档， 但如果使用Requests来研究也许会更有意思一点。我们可以借助Requests的OPTIONS请求类型来看看我们刚使用过的url 支持哪些HTTP方法。</p>

<pre><code class="language-python">&gt;&gt;&gt; verbs = requests.options(r.url)
&gt;&gt;&gt; verbs.status_code
500
</code></pre>

<p>额，这是怎么回事？毫无帮助嘛！原来GitHub，与许多API提供方一样，实际上并未实现OPTIONS方法。 这是一个恼人的疏忽，但没关系，那我们可以使用枯燥的文档。然而，如果GitHub正确实现了OPTIONS， 那么服务器应该在响应头中返回允许用户使用的HTTP方法，例如：</p>

<pre><code class="language-python">&gt;&gt;&gt; verbs = requests.options('http://a-good-website.com/api/cats')
&gt;&gt;&gt; print verbs.headers['allow']
GET,HEAD,POST,OPTIONS
</code></pre>

<p>转而去查看文档，我们看到对于提交信息，另一个允许的方法是POST，它会创建一个新的提交。 由于我们正在使用Requests代码库，我们应尽可能避免对它发送笨拙的POST。作为替代，我们来 玩玩GitHub的Issue特性。</p>

<p>本篇文档是回应*Issue #482*而添加的。鉴于该问题已经存在，我们就以它为例。先获取它。</p>

<pre><code class="language-python">&gt;&gt;&gt; r = requests.get('https://api.github.com/repos/kennethreitz/requests/issues/482')
&gt;&gt;&gt; r.status_code
200
&gt;&gt;&gt; issue = json.loads(r.text)
&gt;&gt;&gt; print issue[u'title']
Feature any http verb in docs
&gt;&gt;&gt; print issue[u'comments']
3
</code></pre>

<p>Cool，有3个评论。我们来看一下最后一个评论。</p>

<pre><code class="language-python">&gt;&gt;&gt; r = requests.get(r.url + u'/comments')
&gt;&gt;&gt; r.status_code
200
&gt;&gt;&gt; comments = r.json()
&gt;&gt;&gt; print comments[0].keys()
[u'body', u'url', u'created_at', u'updated_at', u'user', u'id']
&gt;&gt;&gt; print comments[2][u'body']
Probably in the &quot;advanced&quot; section
</code></pre>

<p>嗯，那看起来似乎是个愚蠢之处。我们发表个评论来告诉这个评论者他自己的愚蠢。那么，这个评论者是谁呢？</p>

<pre><code class="language-python">&gt;&gt;&gt; print comments[2][u'user'][u'login']
kennethreitz
</code></pre>

<p>好，我们来告诉这个叫肯尼思的家伙，这个例子应该放在快速上手指南中。根据GitHub API文档， 其方法是POST到该话题。我们来试试看。</p>

<pre><code class="language-python">&gt;&gt;&gt; body = json.dumps({u&quot;body&quot;: u&quot;Sounds great! I'll get right on it!&quot;})
&gt;&gt;&gt; url = u&quot;https://api.github.com/repos/kennethreitz/requests/issues/482/comments&quot;
&gt;&gt;&gt; r = requests.post(url=url, data=body)
&gt;&gt;&gt; r.status_code
404
</code></pre>

<p>额，这有点古怪哈。可能我们需要验证身份。那就有点纠结了，对吧？不对。Requests简化了多种身份验证形式的使用， 包括非常常见的Basic Auth。</p>

<pre><code class="language-python">&gt;&gt;&gt; from requests.auth import HTTPBasicAuth
&gt;&gt;&gt; auth = HTTPBasicAuth('fake@example.com', 'not_a_real_password')
&gt;&gt;&gt; r = requests.post(url=url, data=body, auth=auth)
&gt;&gt;&gt; r.status_code
201
&gt;&gt;&gt; content = r.json()
&gt;&gt;&gt; print(content[u'body'])
Sounds great! I'll get right on it.
</code></pre>

<p>精彩！噢，不！我原本是想说等我一会，因为我得去喂一下我的猫。如果我能够编辑这条评论那就好了！ 幸运的是，GitHub允许我们使用另一个HTTP动词，PATCH，来编辑评论。我们来试试。</p>

<pre><code class="language-python">&gt;&gt;&gt; print(content[u&quot;id&quot;])
5804413
&gt;&gt;&gt; body = json.dumps({u&quot;body&quot;: u&quot;Sounds great! I'll get right on it once I feed my cat.&quot;})
&gt;&gt;&gt; url = u&quot;https://api.github.com/repos/kennethreitz/requests/issues/comments/5804413&quot;
&gt;&gt;&gt; r = requests.patch(url=url, data=body, auth=auth)
&gt;&gt;&gt; r.status_code
200
</code></pre>

<p>非常好。现在，我们来折磨一下这个叫肯尼思的家伙，我决定要让他急得团团转，也不告诉他是我在捣蛋。 这意味着我想删除这条评论。GitHub允许我们使用完全名副其实的DELETE方法来删除评论。我们来清除该评论。</p>

<pre><code class="language-python">&gt;&gt;&gt; r = requests.delete(url=url, auth=auth)
&gt;&gt;&gt; r.status_code
204
&gt;&gt;&gt; r.headers['status']
'204 No Content'
</code></pre>

<p>很好。不见了。最后一件我想知道的事情是我已经使用了多少限额（ratelimit）。查查看，GitHub在响应头部发送这个信息， 因此不必下载整个网页，我将使用一个HEAD请求来获取响应头。</p>

<pre><code class="language-python">&gt;&gt;&gt; r = requests.head(url=url, auth=auth)
&gt;&gt;&gt; print r.headers
...
'x-ratelimit-remaining': '4995'
'x-ratelimit-limit': '5000'
...
</code></pre>

<p>很好。是时候写个Python程序以各种刺激的方式滥用GitHub的API，还可以使用4995次呢。</p>

<hr />

<h2>响应头链接字段</h2>

<hr />

<p>许多HTTP API都有响应头链接字段的特性，它们使得API能够更好地自我描述和自我显露。</p>

<p>GitHub在API中为 <a href="http://developer.github.com/v3/#pagination">分页</a> 使用这些特性，例如:</p>

<pre><code class="language-python">&gt;&gt;&gt; url = 'https://api.github.com/users/kennethreitz/repos?page=1&amp;per_page=10'
&gt;&gt;&gt; r = requests.head(url=url)
&gt;&gt;&gt; r.headers['link']
'&lt;https://api.github.com/users/kennethreitz/repos?page=2&amp;per_page=10&gt;; rel=&quot;next&quot;, &lt;https://api.github.com/users/kennethreitz/repos?page=6&amp;per_page=10&gt;; rel=&quot;last&quot;'
</code></pre>

<p>Requests会自动解析这些响应头链接字段，并使得它们非常易于使用:</p>

<pre><code class="language-python">&gt;&gt;&gt; r.links[&quot;next&quot;]
{'url': 'https://api.github.com/users/kennethreitz/repos?page=2&amp;per_page=10', 'rel': 'next'}

&gt;&gt;&gt; r.links[&quot;last&quot;]
{'url': 'https://api.github.com/users/kennethreitz/repos?page=7&amp;per_page=10', 'rel': 'last'}
</code></pre>

<hr />

<h2>Transport Adapters</h2>

<hr />

<p>As of v1.0.0, Requests has moved to a modular internal design. Part of the reason this was done was to implement Transport Adapters, originally <a href="http://www.kennethreitz.org/essays/the-future-of-python-http">described here</a>. Transport Adapters provide a mechanism to define interaction methods for an HTTP service. In particular, they allow you to apply per-service configuration.</p>

<p>Requests ships with a single Transport Adapter, the HTTPAdapter. This adapter provides the default Requests interaction with HTTP and HTTPS using the powerful <a href="https://github.com/shazow/urllib3">urllib3</a> library. Whenever a Requests <a href="http://cn.python-requests.org/zh_CN/latest/api.html#requests.Session">Session</a> is initialized, one of these is attached to the <a href="http://cn.python-requests.org/zh_CN/latest/api.html#requests.Session">Session</a> object for HTTP, and one for HTTPS.</p>

<p>Requests enables users to create and use their own Transport Adapters that provide specific functionality. Once created, a Transport Adapter can be mounted to a Session object, along with an indication of which web services it should apply to.</p>

<pre><code class="language-python">&gt;&gt;&gt; s = requests.Session()
&gt;&gt;&gt; s.mount('http://www.github.com', MyAdapter())
</code></pre>

<p>The mount call registers a specific instance of a Transport Adapter to a prefix. Once mounted, any HTTP request made using that session whose URL starts with the given prefix will use the given Transport Adapter.</p>

<p>Many of the details of implementing a Transport Adapter are beyond the scope of this documentation, but take a look at the next example for a simple SSL use- case. For more than that, you might look at subclassing <em>requests.adapters.BaseAdapter</em>.</p>

<h3>Example: Specific SSL Version</h3>

<p>The Requests team has made a specific choice to use whatever SSL version is default in the underlying library (<a href="https://github.com/shazow/urllib3">urllib3</a>). Normally this is fine, but from time to time, you might find yourself needing to connect to a service-endpoint that uses a version that isn’t compatible with the default.</p>

<p>You can use Transport Adapters for this by taking most of the existing implementation of HTTPAdapter, and adding a parameter <em>ssl_version</em> that gets passed-through to <em>urllib3</em>. We’ll make a TA that instructs the library to use SSLv3:</p>

<pre><code class="language-python">import ssl

from requests.adapters import HTTPAdapter
from requests.packages.urllib3.poolmanager import PoolManager


class Ssl3HttpAdapter(HTTPAdapter):
    &quot;&quot;&quot;&quot;Transport adapter&quot; that allows us to use SSLv3.&quot;&quot;&quot;

    def init_poolmanager(self, connections, maxsize, block=False):
        self.poolmanager = PoolManager(num_pools=connections,
                                       maxsize=maxsize,
                                       block=block,
                                       ssl_version=ssl.PROTOCOL_SSLv3)
</code></pre>

<hr />

<h2>Blocking Or Non-Blocking?</h2>

<hr />

<p>With the default Transport Adapter in place, Requests does not provide any kind of non-blocking IO. The <a href="http://cn.python-requests.org/zh_CN/latest/api.html#requests.Response.content"><strong>Response.content</strong></a> property will block until the entire response has been downloaded. If you require more granularity, the streaming features of the library (see <a href="http://cn.python-requests.org/zh_CN/latest/user/advanced.html#streaming-requests">流式请求</a>) allow you to retrieve smaller quantities of the response at a time. However, these calls will still block.</p>

<p>If you are concerned about the use of blocking IO, there are lots of projects out there that combine Requests with one of Python’s asynchronicity frameworks. Two excellent examples are <a href="https://github.com/kennethreitz/grequests">grequests</a> and <a href="https://github.com/ross/requests-futures">requests-futures</a>.</p>

<hr />

<h2>Timeouts</h2>

<hr />

<p>Most requests to external servers should have a timeout attached, in case the server is not responding in a timely manner. Without a timeout, your code may hang for minutes or more.</p>

<p>The connect timeout is the number of seconds Requests will wait for your client to establish a connection to a remote machine (corresponding to the <a href="http://linux.die.net/man/2/connect">connect()</a>) call on the socket. It’s a good practice to set connect timeouts to slightly larger than a multiple of 3, which is the default <a href="http://www.hjp.at/doc/rfc/rfc2988.txt">TCP packet retransmission window</a>.</p>

<p>Once your client has connected to the server and sent the HTTP request, the read timeout is the number of seconds the client will wait for the server to send a response. (Specifically, it’s the number of seconds that the client will wait between bytes sent from the server. In 99.9% of cases, this is the time before the server sends the first byte).</p>

<p>If you specify a single value for the timeout, like this:</p>

<pre><code class="language-python">r = requests.get('https://github.com', timeout=5)
</code></pre>

<p>The timeout value will be applied to both the connect and the read timeouts. Specify a tuple if you would like to set the values separately:</p>

<pre><code class="language-python">r = requests.get('https://github.com', timeout=(3.05, 27))
</code></pre>

<p>If the remote server is very slow, you can tell Requests to wait forever for a response, by passing None as a timeout value and then retrieving a cup of coffee.</p>

<pre><code class="language-python">r = requests.get('https://github.com', timeout=None)
</code></pre>

<hr />

<h2>CA Certificates</h2>

<hr />

<p>By default Requests bundles a set of root CAs that it trusts, sourced from the <a href="https://hg.mozilla.org/mozilla-central/raw-file/tip/security/nss/lib/ckfw/builtins/certdata.txt">Mozilla trust store</a>. However, these are only updated once for each Requests version. This means that if you pin a Requests version your certificates can become extremely out of date.</p>

<p>From Requests version 2.4.0 onwards, Requests will attempt to use certificates from <a href="http://certifi.io/">certifi</a> if it is present on the system. This allows for users to update their trusted certificates without having to change the code that runs on their system.</p>

<p>For the sake of security we recommend upgrading certifi frequently!</p>

<hr />

<blockquote>
<p><strong>说明：前面有些官方文档没翻译到的，我自己翻译了，后一部分，时间太晚了，是在没精力了，以后有时间再翻译，可能我翻译的有些语句不通顺，但是还是能大概表达出意思的，如果你对比了官方文档，觉得你可以翻译得更好，可以私信或留言我哦</strong></p>

<p><strong>想喷我的人也省省吧，的确，这篇文章和之前的一篇Requests安装使用都是我从官网移植过来的，但是我花时间翻译了一部分，排版也废了番功夫，使用MarkDown写成，需要源md文档也可以找我索要，本文随意传播</strong></p>

<hr />

<p><strong>我是Akkuman，同道人可以和我一起交流哦，私信或留言均可,我的博客hacktech.cn | 53xiaoshuo.com</strong></p>

<hr />
</blockquote>

                            </div>
                            <div style="display:block;" class="clearfix">
                                <section style="float:left;">
                                    <span itemprop="keywords" class="tags">
                                        tag(s): <a href="/tag/Python/index.html">Python </a>
                                    </span>
                                </section>
                                <section style="float:right;">
                                    <span><a id="btn-comments" href="javascript:isComments();">show comments</a></span> · <span><a href="javascript:goBack();">back</a></span> · 
                                    <span><a href="/">home</a></span>
                                </section>
                            </div>
                            



<div id="comments" class="gen">
    <script>
        document.write('<section id="disqus_thread"></section>');
        var site_comment_load = function disqus() {
            var d = document, s = d.createElement('script');
            s.src = '//Akkum4n.disqus.com/embed.js';
            s.setAttribute('data-timestamp', +new Date());
            (d.head || d.body).appendChild(s);
        }
    </script>
</div>

                        </article>
                    </div>
                </div>
            </div>
        </div>
        <footer id="footer" role="contentinfo">
    <div class="container-fluid">
        <div class="row">
        <div class="col-12">
            &copy; 
            <script type="text/javascript">
                document.write(new Date().getFullYear());
            </script>
            <a href="/">Akkuman</a>.
            Using <a target="_blank" href="http://www.chole.io/">Ink</a> & <a target="_blank" href="/">Story</a>.
        </div>
        </div>
    </div>
</footer>

<script src="https://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script>
<script src="/bundle/js/prism.js"></script>
<script src="/bundle/js/zoom-vanilla.min.js"></script>
<script src="/bundle/js/main.js"></script>

<script>
    window.onload=function(){
        if (window.location.hash!='') {
          var i=window.location.hash.indexOf('#comment');
          var ii=window.location.hash.indexOf('#respond-post');
          if (i != '-1' || ii != '-1') {
            document.getElementById('btn-comments').innerText='hide comments';
            document.getElementById('comments').style.display='block';
          }
        }
    }

    function isMenu(){
        if(document.getElementById('menu-1').style.display=='inline'||document.getElementById('menu-1').style.display=='block'){
            $('#search-box').fadeOut(200);
            $('#menu-page').fadeOut(200);
            $('#menu-1').fadeOut(500);
            $('#menu-2').fadeOut(400);
            $('#menu-3').fadeOut(300);
        } else {
            $('#menu-1').fadeIn(150);
            $('#menu-2').fadeIn(150);
            $('#menu-3').fadeIn(150);
        }
    }

    function isMenu1(){
        if(document.getElementById('menu-page').style.display=='block'){
            $('#menu-page').fadeOut(300);
        } else {
            $('#menu-page').fadeIn(300);
        }
    }

    function isMenu2(){
        if(document.getElementById('torTree')){
            if(document.getElementById('torTree').style.display=='block'){
                $('#torTree').fadeOut(300);
            } else {
                $('#torTree').fadeIn(300);
            }
        }
    }

    function isMenu3(){
        if(document.getElementById('search-box').style.display=='block'){
            $('#search-box').fadeOut(300);
        } else {
            $('#search-box').fadeIn(300);
        }
    }

    function isComments(){
        if(document.getElementById('btn-comments').innerText=='show comments'){
            document.getElementById('btn-comments').innerText='hide comments';
            document.getElementById('comments').style.display='block';
            site_comment_load();
        } else {
            document.getElementById('btn-comments').innerText='show comments';
            document.getElementById('comments').style.display='none';
        }
    }

    function Search404(){
        $('#menu-1').fadeIn(150);
        $('#menu-2').fadeIn(150);
        $('#menu-3').fadeIn(150);
        $('#search-box').fadeIn(300);
    }

    function goBack(){
        window.history.back();
    }
</script>


<script async>
"use strict";
(function(){
var cp = function(){
    var len = cPlayerOptions.length;
    for(var i=0;i<len;i++){
        var element = document.getElementById('player' + cPlayerOptions[i]['id'])
        while (element.hasChildNodes()) {
            element.removeChild(element.firstChild);
        };
        cPlayers[i] = new cPlayer({
            element: element,
            list: cPlayerOptions[i]['list'],
            });
    };
    cPlayers = [];cPlayerOptions = [];
};
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "https://cdn.bootcss.com/cplayer/3.2.1/cplayer.js";
script.async = true;
if(script.readyState){  
    script.onreadystatechange = function(){
        if (script.readyState == "loaded" ||
            script.readyState == "complete"){
            script.onreadystatechange = null;
            cp();
        }
    };
}else{  
    script.onload = function(){
        cp();
    };
}
document.head.appendChild(script);
})();
</script>

    </body>
</html>
